/*
 * InvulgoTracker
 * Copyright (C) Thomas Andersen 2011 <thomas.1.andersen@gmail.com>
 * 
 * InvulgoTracker is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * InvulgoTracker is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>

#include <config.h>

#include "gettextmacro.h"
#include "task-view.h"
#include "summary-view.h"
#include "day.h"
#include "invulgotrackerapp.h"
#include "prefs.h"

SummaryView::SummaryView()
{
    prevTime = NULL;
    prevName = NULL;
}


SummaryView::~SummaryView()
{
    Clean();
}

void SummaryView::Clean()
{
    g_object_unref (model);
    gtk_tree_view_set_model (view, NULL);
    while (gtk_tree_view_remove_column(view, gtk_tree_view_get_column(view, 0)) > 0);
}

void SummaryView::Restart()
{
    Clean();
    Init(GTK_WIDGET(view), isSortable, isShowingPct);
}

GtkTreeModel *SummaryView::CreateModel()
{
    GtkListStore  *store;

    store = gtk_list_store_new (NUM_COLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_LONG, G_TYPE_LONG);

    return GTK_TREE_MODEL (store);
}


extern "C" gint SummaryView::sort_iter_compare_func_title(GtkTreeModel *model, GtkTreeIter  *a, GtkTreeIter  *b, gpointer userdata)
{
    SummaryView *sv = (SummaryView*)userdata;
    return sv->sort_iter_compare_func(model, a, b, GINT_TO_POINTER(SORT_TITLE));
}

extern "C" gint SummaryView::sort_iter_compare_func_time(GtkTreeModel *model, GtkTreeIter  *a, GtkTreeIter  *b, gpointer userdata)
{
    SummaryView *sv = (SummaryView*)userdata;
    return sv->sort_iter_compare_func(model, a, b, GINT_TO_POINTER(SORT_TIME));
}


gint SummaryView::sort_iter_compare_func (GtkTreeModel *model, GtkTreeIter  *a, GtkTreeIter  *b, gpointer userdata)
{
    gint sortcol = GPOINTER_TO_INT(userdata);
    gint ret = 0;
    glong type1 = 0,type2 = 0;


    /* First sort by type. */
    gtk_tree_model_get(model, a, COL_TYPE, &type1, -1);
    gtk_tree_model_get(model, b, COL_TYPE, &type2, -1);
    if (type1 != type2) {
        if (type1 < type2)
            ret = 1;
        else
            ret = -1;
        GtkSortType      order;
        gint             sortid;
        if (gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(model), &sortid, &order)) {
            if (order == GTK_SORT_ASCENDING)
                ret *= -1;
        }
    } else {

        /* Sort by content. */
        switch (sortcol)
        {
        case SummaryView::SORT_TITLE:
            {
                gchar *name1, *name2;

                gtk_tree_model_get(model, a, SummaryView::COL_TITLE, &name1, -1);
                gtk_tree_model_get(model, b, SummaryView::COL_TITLE, &name2, -1);

                if (name1 == NULL || name2 == NULL)
                {
                    if (name1 == NULL && name2 == NULL)
                        break; /* both equal => ret = 0 */

                    ret = (name1 == NULL) ? -1 : 1;
                }
                else
                {
                    ret = g_utf8_collate(name1,name2);
                }

                g_free(name1);
                g_free(name2);
            }
            break;
        case SummaryView::SORT_TIME:
            {
                gchar *name1, *name2;

                gtk_tree_model_get(model, a, SummaryView::COL_TIME, &name1, -1);
                gtk_tree_model_get(model, b, SummaryView::COL_TIME, &name2, -1);

                if (name1 == NULL || name2 == NULL)
                {
                    if (name1 == NULL && name2 == NULL)
                        break; /* both equal => ret = 0 */

                    ret = (name1 == NULL) ? -1 : 1;
                }
                else
                {
                    TaskTime t1(name1);
                    TaskTime t2(name2);
                    if (t1 < t2)
                        ret = -1;
                    else if (t1 == t2)
                        ret = 0;
                    else
                        ret = 1;
                }

                g_free(name1);
                g_free(name2);
            }
            break;

        default:
            g_return_val_if_reached(0);
        }
    }
    return ret;
}

extern "C" void SummaryView::column_title_clicked(GtkTreeViewColumn *treeviewcolumn, gpointer user_data)
{
    SummaryView *sv = (SummaryView*)user_data;
    GtkTreeSortable *sortable;
    GtkSortType      order;
    gint             sortid;

    sortable = GTK_TREE_SORTABLE(sv->model);

    /* If we are already sorting by year, reverse sort order,
     *  otherwise set it to year in ascending order */
    g_object_set(treeviewcolumn, "sort-indicator", TRUE ,NULL);
    if (gtk_tree_sortable_get_sort_column_id(sortable, &sortid, &order) == TRUE &&  sortid == SORT_TITLE) {
        GtkSortType neworder;
        neworder = (order == GTK_SORT_ASCENDING) ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
        gtk_tree_sortable_set_sort_column_id(sortable, SORT_TITLE, neworder);
        g_object_set(treeviewcolumn, "sort-order", neworder ,NULL);
    } else {
        GList *list = gtk_tree_view_get_columns(sv->view);
        do {
            if (list && list->data != treeviewcolumn)
                g_object_set(list->data, "sort-indicator", FALSE ,NULL);
        } while((list = g_list_next(list)));
        g_list_free(list);
        gtk_tree_sortable_set_sort_column_id(sortable, SORT_TITLE, GTK_SORT_ASCENDING);
        g_object_set(treeviewcolumn, "sort-order", GTK_SORT_ASCENDING ,NULL);
    }
}

extern "C" void SummaryView::column_time_clicked(GtkTreeViewColumn *treeviewcolumn, gpointer user_data)
{
    SummaryView *sv = (SummaryView*)user_data;
    GtkTreeSortable *sortable;
    GtkSortType      order;
    gint             sortid;

    sortable = GTK_TREE_SORTABLE(sv->model);

    /* If we are already sorting by year, reverse sort order,
     *  otherwise set it to year in ascending order */

    g_object_set(treeviewcolumn, "sort-indicator", TRUE ,NULL);
    if (gtk_tree_sortable_get_sort_column_id(sortable, &sortid, &order) == TRUE &&  sortid == SORT_TIME) {
        GtkSortType neworder;
        neworder = (order == GTK_SORT_ASCENDING) ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
        gtk_tree_sortable_set_sort_column_id(sortable, SORT_TIME, neworder);
        g_object_set(treeviewcolumn, "sort-order", neworder ,NULL);
    } else {
        GList *list = gtk_tree_view_get_columns(sv->view);
        do {
            if (list && list->data != treeviewcolumn)
                g_object_set(list->data, "sort-indicator", FALSE ,NULL);
        } while((list = g_list_next(list)));
        g_list_free(list);
        gtk_tree_sortable_set_sort_column_id(sortable, SORT_TIME, GTK_SORT_ASCENDING);
        g_object_set(treeviewcolumn, "sort-order", GTK_SORT_ASCENDING ,NULL);
    }
}

void SummaryView::Init(GtkWidget *view, gboolean sortable, gboolean showpct)
{
    GtkCellRenderer *renderer;

    this->isSortable = sortable;
    isShowingPct = showpct;
    this->view = GTK_TREE_VIEW(view);

    model = CreateModel();
    renderer = gtk_cell_renderer_text_new ();
    GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(_("Item"),  renderer, "text", COL_TITLE, NULL);
    //    gtk_tree_view_column_set_resizable(column, TRUE);
    g_object_set(column, "sizing",GTK_TREE_VIEW_COLUMN_AUTOSIZE,NULL);
    if (sortable) {
        g_object_set(column, "reorderable", TRUE ,NULL);
        g_object_set(column, "clickable", TRUE ,NULL);
        g_object_set(column, "sort-indicator", TRUE ,NULL);
        g_signal_connect(G_OBJECT(column), "clicked", G_CALLBACK(column_title_clicked), this);
    }
    gtk_tree_view_insert_column(GTK_TREE_VIEW (view), column, -1);

    Prefs prefs;

    if (!showpct || !prefs.GetShowSummaryPercentage()) {
        renderer = gtk_cell_renderer_text_new ();
        g_object_set(renderer, "xalign",1.0,NULL);
        pctColumn = column = gtk_tree_view_column_new_with_attributes(_("Time"),  renderer, "text", COL_TIME, NULL);
    } else {
        renderer = gtk_cell_renderer_progress_new();
        g_object_set(renderer, "text-xalign",1.0,NULL);
        pctColumn = column = gtk_tree_view_column_new_with_attributes(_("Time"),  renderer, "text", COL_TIME, "value", COL_VALUE, NULL);
    }
    if (sortable) {
        g_object_set(column, "reorderable", TRUE ,NULL);
        g_object_set(column, "clickable", TRUE ,NULL);
        g_signal_connect(G_OBJECT(column), "clicked", G_CALLBACK(column_time_clicked), this);
        GtkTreeSortable *sortable = GTK_TREE_SORTABLE(model);
        gtk_tree_sortable_set_sort_func(sortable, SORT_TITLE, sort_iter_compare_func_title,this, NULL);
        gtk_tree_sortable_set_sort_func(sortable, SORT_TIME, sort_iter_compare_func_time,this, NULL);
        /* set initial sort order */
        gtk_tree_sortable_set_sort_column_id(sortable, SORT_TITLE, GTK_SORT_ASCENDING);
    }
    gtk_tree_view_insert_column(GTK_TREE_VIEW (view), column, -1);

    gtk_tree_view_set_model (GTK_TREE_VIEW (view), model);

}

void SummaryView::CalcDay(guint year, guint month, guint day, gboolean accumulate)
{
    GtkTreeIter iter = {0};

    lastTaskId = 0;

    if (!accumulate) {
        gtk_list_store_clear(GTK_LIST_STORE(model));

        gtk_list_store_append (GTK_LIST_STORE(model), &iter);
        gtk_list_store_set (GTK_LIST_STORE(model), &iter,
                            COL_TYPE, TYPE_DAY_TITLE,
                            COL_TITLE, _("Today:"),
                            COL_TIME,  "----------",
                            COL_ID, NULL,
                            COL_VALUE , 0,
                            -1);
    }
    
    Day dayloader(year, month, day);

    totalTime.TimeFromString((char*)"00:00");
    currentType = TYPE_DAY;
    
    dayloader.Load(this);

    if (prevTime) {
        delete prevTime;
        prevTime = NULL;
    }

    if (prevName) {
        free(prevName);
        prevName = NULL;
    }

    if (!accumulate) {
        char *timeText = totalTime.TimeText();
        gtk_list_store_append (GTK_LIST_STORE(model), &iter);
        gtk_list_store_set (GTK_LIST_STORE(model), &iter,
                            COL_TYPE, TYPE_DAYTOTAL,
                            COL_TITLE, _("Total"),
                            COL_TIME, timeText,
                            COL_ID, NULL,
                            COL_VALUE , 0,
                            -1);
        free(timeText);

        gtk_list_store_append (GTK_LIST_STORE(model), &iter);
        gtk_list_store_set (GTK_LIST_STORE(model), &iter,
                            COL_TYPE, TYPE_DIVIDER,
                            COL_TITLE, "----------",
                            COL_TIME,  "----------",
                            COL_ID, NULL,
                            COL_VALUE , 0,
                            -1);
    }
    Prefs prefs;

    if (!accumulate && prefs.GetShowSummaryPercentage()) {
        CalcPct(TYPE_DAY);
    }

    if (!accumulate) {
        CalcMonth(year, month);
        if (prefs.GetShowSummaryPercentage())
            CalcPct(TYPE_MONTH);
    }

}

void SummaryView::CalcPct(glong type)
{
    GtkTreeIter iter;

    if (!totalTime.Minutes())
        return;
    if (gtk_tree_model_get_iter_first(model, &iter)) {
        gchar *timeStr;
        glong itemType;

        do {
            timeStr = NULL;
            itemType = TYPE_NONE;
            gtk_tree_model_get(model, &iter,
                               COL_TIME, &timeStr,
                               COL_TYPE, &itemType,
                               -1);
            if (type == itemType) {
                TaskTime tt(timeStr);

                glong pct = (glong)(((gdouble)tt.Minutes() / (gdouble)totalTime.Minutes()) * 100.0);
                if (pct > 100)
                    pct = 100;
                gtk_list_store_set (GTK_LIST_STORE(model), &iter, COL_VALUE, pct, -1);
            }
            g_free(timeStr);
        } while(gtk_tree_model_iter_next(model, &iter));
    }
}

void SummaryView::CalcMonth(guint year, guint month)
{
    GtkTreeIter iter = {0};

    Day dayfinder(year, month, -1);

    gtk_list_store_append (GTK_LIST_STORE(model), &iter);
    gtk_list_store_set (GTK_LIST_STORE(model), &iter,
                        COL_TYPE, TYPE_MONTH_TITLE,
                        COL_TITLE, _("This month:"),
                        COL_TIME,  "----------",
                        COL_ID, NULL,
                        COL_VALUE , 0,
                        -1);
    
    totalTime.TimeFromString((char*)"00:00");
    currentType = TYPE_MONTH;
    dayfinder.FindFiles();
    int y,m,d;
    while(dayfinder.FindNext(y,m,d)) {
        Day dayloader(y,m,d);
        dayloader.Load(this);

        if (prevTime) {
            delete prevTime;
            prevTime = NULL;
        }

        if (prevName) {
            free(prevName);
            prevName = NULL;
        }
    }

    char *timeText = totalTime.TimeText();
    gtk_list_store_append (GTK_LIST_STORE(model), &iter);
    gtk_list_store_set (GTK_LIST_STORE(model), &iter,
                        COL_TYPE, TYPE_MONTHTOTAL,
                        COL_TITLE, _("Month total"),
                        COL_TIME, timeText,
                        COL_ID, NULL,
                        COL_VALUE , 0,
                        -1);
    free(timeText);
}

void SummaryView::Data(gchar *name, gchar *timeStr, TaskId id)
{
    taskTime.TimeFromString(timeStr);
    free(timeStr);
    if (currentType == TYPE_DAY) {
        lastTaskId = id;
        lastTime = taskTime;
    }
    if (prevTime) {
        TaskTime diff = taskTime.SubTime(*prevTime);
        TaskId parentId = prevId;
        gchar *parentName;
        if (prevName) {
            AddToTask(diff, prevName, prevId);
            prevName = NULL;
            while(InvulgoTrackerApp::app->GetTaskView().GetParent(parentId, &parentName))
                AddToTask(diff, parentName, parentId);
        }
        totalTime = totalTime.AddTime(diff);
        delete prevTime;
        prevTime = NULL;
    }
    prevTime = new TaskTime(taskTime);
    if (prevName)
        free(prevName);
    prevName = name;
    prevId = id;
}


void SummaryView::AddToTask(TaskTime diff, gchar *taskName, TaskId id)
{
    GtkTreeIter iter;
    gboolean existed = FALSE;

    if (gtk_tree_model_get_iter_first(model, &iter)) {
        gchar *timeStr;
        gchar *value;
        glong type;

        do {
            timeStr = NULL;
            value = NULL;
            type = TYPE_NONE;
            gtk_tree_model_get(model, &iter,
                               COL_TIME, &timeStr,
                               COL_ID, &value,
                               COL_TYPE, &type,
                               -1);
            TaskId ti(value);
            g_free(value);
            if (type == currentType && id == ti) {
                existed = TRUE;
                TaskTime taskTime;
                taskTime.TimeFromStringNoCheck(timeStr);
                taskTime = taskTime.AddTime(diff);
                char *timeText = taskTime.TimeText();
                gtk_list_store_set (GTK_LIST_STORE(model), &iter, COL_TIME, timeText, -1);
                free(timeText);
                free(timeStr);
                free(taskName);
                break;
            }
            free(timeStr);
        } while(gtk_tree_model_iter_next(model, &iter));
    }

    if (!existed) {
        char *timeText = diff.TimeText();
        gtk_list_store_append (GTK_LIST_STORE(model), &iter);
        gtk_list_store_set (GTK_LIST_STORE(model), &iter,
                            COL_TYPE, currentType,
                            COL_TITLE, taskName,
                            COL_ID, (const gchar *)id,
                            COL_TIME, timeText,
                            COL_VALUE, 0,
                            -1);
        g_free(taskName);
        free(timeText);
    }

}


bool SummaryView::GetFirst(gchar **name, gchar **timeStr)
{
    if (gtk_tree_model_get_iter_first(model, &currentIter)) {
        gtk_tree_model_get(model, &currentIter,
                           COL_TITLE, name,
                           COL_TIME, timeStr,
                           -1);
        return true;
    }
    return false;
}

bool SummaryView::GetNext(gchar **name, gchar **timeStr)
{
    if (gtk_tree_model_iter_next(model, &currentIter)) {
        gtk_tree_model_get(model, &currentIter,
                           COL_TITLE, name,
                           COL_TIME, timeStr,
                           -1);
        return true;
    }
    return false;
}




